#include <iostream>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <utility>
#include <algorithm>
using namespace std;

struct Comsumer {
    string name;
    int needs[8928] = { 0 };//need[]括号中是不同时刻，从0开始   意思就是每个用户的不同时刻的需求!!!!!!!!!!!!!!!!!!!!!!!!!
}comsumer[35];//用户节点，从0开始

struct Site {
    string name;
    int limit = 0;
}site[135];//边缘节点，从0开始

int Qos[35][135];//节点之间qos延迟  这里有点难受左边下标表示用户节点 右边表示边缘节点

int indexForComsumer = 0;//客户节点总数
int indexForSite = 0;//边缘节点总数
int indexForTime = 0;//表示时间节点(时刻)总数（已自动统计）

int Qoslimit = 0;//Qos约束条件

int maxPeakTime;//每一个节点 最多能造多少次大峰（再多就不能保证峰在95点右侧了）

/*注意一下！！如果后续想要使用用户节点的编号或者边缘节点的编号,其数组下表就是它的编号,这一点会在以下读取qos.csv中,就是读取网络延迟的数据结构中十分重要*/

//Comsumer null = { "null" };//不要管无意义，用于函数返回空
//Site Null = { "null" };//不要管无意义，用于函数返回空
//
//Comsumer findComsumerById(string cname) {
//    for (int i = 0; i < indexForComsumer; i++) {
//        if (comsumer[i].name == cname) {
//            return comsumer[i];
//        }
//    }
//    cout << "没找到该ID的用户节点" << endl;
//    return null;
//}
//
//Site findSiteById(string sname) {
//    for (int i = 0; i < indexForSite; i++) {
//        if (site[i].name == sname) {
//            return site[i];
//        }
//    }
//    cout << "没找到该ID的边缘节点" << endl;
//    return Null;
//}

int getTotalComsumer() {
    return indexForComsumer;
}
int getTotalSite() {
    return indexForSite;
}
int getTotalTime() {
    return indexForTime;
}

void initialize() {
    //首先准备读取demand.csv包含客户节点的名字，数量，每个客户节点在每一时刻的带宽需求----------------------------------------------------------------

    ifstream inFile;
    //inFile.open("/data/demand.csv", ios::in);//文件均在/data目录下
    inFile.open("demand.csv", ios::in);//文件均在/data目录下

    if (!inFile.is_open()) {
        cout << "1打开文件失败" << endl;
        return;
    }

    string line;
    string temp;

    //以下输入用户名
    getline(inFile, line);
    istringstream sin(line);

    getline(sin, temp, ','); //将字符串流sin中的字符读入到field字符串中，以逗号为分隔符
    while (getline(sin, temp, ',')) {
        comsumer[indexForComsumer].name = temp;
        indexForComsumer++;
    }

    sin.clear();

    //以下输入用户中的带宽需求
    while (getline(inFile, line)) {

        istringstream sin1(line);

        getline(sin1, temp, ',');//把第一列吸了

        int tempIndexForComsumer = 0;
        while (getline(sin1, temp, ',')) {
            int tempnum = atoi(temp.c_str());
            comsumer[tempIndexForComsumer].needs[indexForTime] = tempnum;
            tempIndexForComsumer++;
        }


        indexForTime++;
        sin1.clear();
    }

    sin.clear();//清空字符串流
    inFile.close();//关闭文件


    //接下来读入site_bandwidth.csv文件----------------------------------------------------------------

    //inFile.open("/data/site_bandwidth.csv", ios::in);
    inFile.open("site_bandwidth.csv", ios::in);

    if (!inFile.is_open()) {
        cout << "2打开文件失败" << endl;
        return;
    }

    getline(inFile, line);//把第一行吃了

    while (getline(inFile, line)) {

        istringstream sin2(line);

        getline(sin2, temp, ',');
        site[indexForSite].name = temp;

        getline(sin2, temp, ',');
        int tempnum = atoi(temp.c_str());
        site[indexForSite].limit = tempnum;
        indexForSite++;

        sin2.clear();
    }

    inFile.close();

    //再接下来读入qos.csv文件---------------------------------------------------------------------------------

    //inFile.open("/data/qos.csv", ios::in);
    inFile.open("qos.csv", ios::in);

    if (!inFile.is_open()) {
        cout << "2打开文件失败" << endl;
        return;
    }

    getline(inFile, line);//把第一行吃了
    int QosIndexForSite = 0;

    while (getline(inFile, line)) {
        istringstream sin3(line);

        getline(sin3, temp, ',');//把第一列吸了

        int QosIndexForComsumer = 0;

        while (getline(sin3, temp, ',')) {
            int tempnum = atoi(temp.c_str());
            Qos[QosIndexForComsumer][QosIndexForSite] = tempnum;
            QosIndexForComsumer++;
        }

        QosIndexForSite++;

        sin3.clear();
    }

    inFile.close();

    //最后读入Qos--------------------------------------------------------------------偷了点懒，可能会出事，不过应该没问题
    //inFile.open("/data/config.ini", ios::in);
    inFile.open("config.ini", ios::in);

    getline(inFile, line);
    getline(inFile, line);
    istringstream sin4(line);
    getline(sin4, temp, '=');
    getline(sin4, temp, '=');
    int tempnum1 = atoi(temp.c_str());
    Qoslimit = tempnum1;

    sin4.clear();

    inFile.close();

    //--------------------------------------------------------------------------------------------------------------------------------------

    return;
}

//////////////////////////////////////////////////////////////////////////////////////

class FlowChart {
public:
    static int c_size, s_size;//全局量 c、s
    static int PeakTimeofSite[135];//记录每个节点全局大峰数
    int timeSpot = 0;
public:
    FlowChart() {//初始化表格
        chart = new int* [c_size];
        for (int i = 0; i < c_size; i++)
            chart[i] = new int[s_size];
        outF = new int* [c_size];
        for (int i = 0; i < c_size; i++) {
            outF[i] = new int[s_size];
        }
        for (int i = 0; i < c_size; i++) {
            for (int j = 0; j < s_size; j++) {
                outF[i][j] = 0;
            }
        }

        for (int i = 0; i < s_size; i++) {
            m_limit[i] = site[i].limit;
        }
    }
    void getData(int tt) {//把t时刻数据录入fc
        timeSpot = tt;
        for (int i = 0; i < c_size; i++) {
            for (int j = 0; j < s_size; j++) {
                if (Qos[i][j]) {
                    inPut(i, j, comsumer[i].needs[tt]);
                    available[i]++;
                }
                else inPut(i, j, -1);
            }
        }
        //////////////////
        /*ofstream fout("chart.txt", ios::out);
        for (int i = 0; i < c_size; i++) {
            for (int j = 0; j < s_size; j++) {
                fout << chart[i][j] << ' ';
            }
            fout << endl;
        }*/
        //////////////////
    }
    void StartCalculate() {
        //        int times = s_size/10+1;
        //
        //        while (times--) {//每一个时刻造times个大峰
        //            int value, MaxCol;//value：最大峰值    MaxCol:最大峰site
        //            pair<int, int> p = findMaxColumn();//找出下一个可行最大峰 返回value和MaxCol
        //            value = p.first;
        //            MaxCol = p.second;
        //
        //            if (value == -1 || MaxCol == -1)break;//存在这样一种情况，每个时刻造出的峰没有达到times上限，但是已经不能再造峰了（超过了MaxPeakTime）
        //
        //            boolPeak[MaxCol] = 1;
        //
        //            for (int i = 0; i < c_size; i++) {//给峰分配流量
        //                int Bo = getBoolPeak();
        //                // int flow=min(getNeed(i)-available[i]+Bo, value-available[i]+Bo);
        //                int flow = min(getNeed(i), value);
        //                if (flow < 0)flow = 0;
        //                distribution(i, flow, MaxCol);//大哥吃肉
        //
        //                // for(int j=0;j<s_size;j++){
        //                // 	if(j==MaxCol)continue;
        //                // 	if(chart[i][j]==-1)continue;//本质上是qos表
        //                // 	distribution(i, 1, j);//兄弟喝汤
        //                // }
        //
        //                value -= flow;
        //                if (value == 0)break;
        //                if (value < 0) {
        //                    cout << "warning!!!!\n";
        //                }
        //            }
        //        }

        AveragePlan();
    }

    void fileOut() {
        ofstream outFile;

        //outFile.open("/output/solution.txt", ios::app);
        outFile.open("solution.txt", ios::app);

        for (int i = 0; i < c_size; i++) {
            outFile << comsumer[i].name << ':';
            int indexForLoop = 0;
            //            int summmmmm = 0;
            for (int j = 0; j < s_size; j++) {
                if (outF[i][j] == 0) {
                    continue;
                }
                if (indexForLoop == 0) {}
                else {
                    outFile << ',';
                }
                if (outF[i][j] != 0) {
                    //                    summmmmm += outF[i][j];
                    outFile << '<' << site[j].name << ',' << outF[i][j] << '>';
                    indexForLoop++;
                }
            }
            //            outFile << "---------" << summmmmm;
            outFile << '\n';
        }

        outFile.close();
    }

    void printChart(string str = "chart.txt") {
        ofstream fout(str, ios::app);
        for (int i = 0; i < c_size; i++) {
            for (int j = 0; j < s_size; j++) {
                fout << chart[i][j] << ' ';
            }
            fout << endl;
        }
    }

    ~FlowChart() {
        for (int i = 0; i < c_size; i++) {
            delete[] chart[i];
            delete[] outF[i];
        }

        delete[] chart;
        delete[] outF;
    }

private:
    int** outF = NULL;
    int** chart = NULL;//每一个时刻的流量图
    int sumofColumn[135] = { 0 };//记录列求和max
    int available[35] = { 0 };//每一客户的实际可用节点数目
    bool boolPeak[135] = { 0 };//是否已经在本时刻计算过大峰 计算过记为1 本时刻不参与下一轮计算
    int m_limit[35] = { 0 };//动态limit

    void inPut(const int c, const int s, const int value) {//输入口
        chart[c][s] = value;
    }

    void getSumofEachColumn() {
        int temp = 0;
        for (int i = 0; i < s_size; i++) {
            if (PeakTimeofSite[i] >= maxPeakTime)continue;//无法保证95
            if (boolPeak[i])continue;//本时刻已经造过峰了
            for (int j = 0; j < c_size; j++) {
                if (chart[j][i] == -1)continue;
                temp += chart[j][i];
            }
            sumofColumn[i] = min(temp, site[i].limit);
        }
    }
    void AveragePlan() {//平均方案处理
        for (int i = 0; i < c_size; i++) {
            while(getNeed(i)){
                if(getNeed(i) < available[i]){//特殊情况2
                    for(int j=0;j<s_size;j++){
                        if(chart[i][j]==-1)continue;
                        if(m_limit[i]>=1){
                            distribution(i, 1, j);
                        }
                    }
                }
                int HugeFlow;
                int flow1 = getAveragePlan(i);//根据site最小limit
                int flow2 = getNeed(i) / available[i];//根据客户实际需要

                if (flow1 >= flow2) {//最小site也吃得下
                    HugeFlow = flow2;
                }
                else{
                    HugeFlow=flow1;
                }
                for (int j = 0; j < s_size; j++) {//平均分
                    if (boolPeak[j])continue;
                    if (chart[i][j] == -1)continue;

                    distribution(i, HugeFlow, j);
                }
            }

            if (!getNeed(i)) {
                cout << "Site's storage warning!!!\n";
                break;
            }
        }
    }
    pair<int, int> findMaxColumn() {
        getSumofEachColumn();
        int id = -1; int flag = -1;
        for (int i = 0; i < s_size; i++) {
            if (boolPeak[i])continue;//本时刻已经造过峰了
            if (sumofColumn[i] > flag) {
                flag = sumofColumn[i];
                id = i;
            }
        }
        return make_pair(flag, id);
    }

    int getNeed(int c) {
        for (int i = 0; i < s_size; i++) {
            if (chart[c][i] != -1)return chart[c][i];
        }
        return 0;
    }

    int getBoolPeak() {
        int sum = 0;
        for (int i = 0; i < s_size; i++) {
            if (boolPeak[i])sum++;
        }
        return sum;
    }

    int getAveragePlan(int i) {//共有平摊值
        int MaxValue = 0x7fffffff;
        for (int j = 0; j < s_size; j++) {
            if (chart[i][j] == -1)continue;
            if(m_limit[j]==0)continue;
            MaxValue = MaxValue < m_limit[j] ? MaxValue : m_limit[j];
        }

        return MaxValue;
    }

    void distribution(const int c, const int x, const int s) {//分配流量 c的x给s
        for (int i = 0; i < s_size; i++) {
            if (chart[c][i] == -1)continue;
            else {
                if (chart[c][i] - x < 0) {
                    continue;
                }
                chart[c][i] -= x;
            }
        }
        m_limit[s] -= x;//分配流量之后 site可用减少
        solve_log(c, x, s);//生成日志
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void solve_log(int c, int x, int s) {//每次分配流量都会通过这个接口生成log，c-->x不唯一 所以是+=
        outF[c][s] += x;
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
};
int FlowChart::c_size = 0;
int FlowChart::s_size = 0;
int FlowChart::PeakTimeofSite[135] = { 0 };

void from_QOS_to_DEATH() {//qos只有可行与不可行
    for (int i = 0; i < FlowChart::c_size; i++) {
        for (int j = 0; j < FlowChart::s_size; j++) {
            if (Qos[i][j] > Qoslimit) {//qos不合条件 舍去
                Qos[i][j] = 0;
            }
            else Qos[i][j] = 1;
        }
    }
}


int main() {
    initialize();

    /*ofstream fout("limit.txt", ios::out);
    for (int i = 0; i < indexForSite; i++) {
        fout << site[i].limit;
        fout << endl;
    }*/

    FlowChart::c_size = getTotalComsumer();
    FlowChart::s_size = getTotalSite();
    int totalTime = getTotalTime();//全局建模

    from_QOS_to_DEATH();
    maxPeakTime = totalTime / 20;//数据的初步处理

    for (int i = 0; i < totalTime; i++) {//处理每时刻
        FlowChart fc;
        fc.getData(i);

        fc.StartCalculate();
        fc.fileOut();
    }
    return 0;
}